﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using NLog;

namespace TradingServer.Core.Entities
{
    public enum OrderState
    {
        Opened,
        PartiallyClosed,
        Closed,
		Canceled
    }

    public enum OrderType
    {
        Buy,
        Sell,
        StopLoss,
        TakeProfit
    }

    public class Order : IComparable<Order>
    {
        public Order(OrderType type, Security security, decimal volume, decimal? price = null)
        {
			Id = Guid.NewGuid();
            _orderType = type;
            _volume = volume;
            _security = security;
            _price = price;
			CreateDate = DateTimeOffset.Now;
        }

        public OrderGlass Glass { get { return OrderGlassFactory.Get(_security); } }
        public OrderState State { get { return _currentState; } }

		public Guid Id { get; private set; }
		public DateTimeOffset CreateDate { get; private set; }
		public Account Account { get; set; }
        public Security Security { get { return _security; } }
		public IEnumerable<Trade> Trades { get { lock (_tradesLocker) return _trades; } }

        public event Action<Order, EventArgs> StateChanged;
        public event Action<Trade> TradePerformed;

		public void Put(TimeSpan? timeToLive)
		{
			if (timeToLive.HasValue)
				_cancellationTimer = new Timer(OnTimerElapsed, this, (long) timeToLive.Value.TotalMilliseconds, Timeout.Infinite);
			Put();
		}

        public void Put()
        {
            Glass.Put(this);
        }

        public void Cancel()
        {
			Glass.Remove(this);
			if (_currentState == OrderState.Opened)
			{
				if (Trades.Count() != 0)
				{
					_currentState = OrderState.PartiallyClosed;
				}
				else
				{
					_currentState = OrderState.Canceled;
				}
				StateChanged.SafeInvoke(this, EventArgs.Empty);
			}
        }

		public override int GetHashCode()
		{
			return Id.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			if (obj == null) return false;
			if (!(obj is Order)) return false;
			return Id.Equals((obj as Order).Id);
		}

		public override string ToString()
		{
			return string.Format("Order [ {2}, Price: {0}, RVolume: {1} ]", Price, RemainingVolume, Type);
		}

		#region IComparable<Order> Members

		public int CompareTo(Order other)
		{
			return new OrderComparer().Compare(this, other);
		}

		#endregion

        #region Internal & Protected members

        internal decimal? Price { get { return _price; } }
        internal OrderType Type { get { return _orderType; } }
        internal decimal Volume { get { return _volume; } }
		internal decimal CurrentVolume { get { lock (_tradesLocker) return _trades.Sum(item => item.Volume); } }
		internal decimal RemainingVolume { get { return Volume - CurrentVolume; } }

		internal void AddTrade(Trade newTrade)
		{
			TradePerformed.SafeInvokeAsync(newTrade);

			lock (_tradesLocker)
			{
				_trades.Add(newTrade);
				if (Account != null) Account.Briefcase.Add(Trades);
				if (CurrentVolume == Volume) Close();
			}
		}
		
        #endregion

        #region Private members

		private readonly Logger Log = LogManager.GetCurrentClassLogger();

        private OrderState _currentState = OrderState.Opened;
		private readonly List<Trade> _trades = new List<Trade>();
        private readonly decimal _volume;
        private readonly Security _security;
        private readonly OrderType _orderType;
        private readonly decimal? _price;
		private Timer _cancellationTimer;
    	private readonly object _timerLocker = new object();
    	private readonly object _tradesLocker = new object();

		private void OnTimerElapsed(object state)
		{
			Cancel();
			lock (_timerLocker)
			{
				if (_cancellationTimer == null) return;
				_cancellationTimer.Dispose();
				_cancellationTimer = null;
			}
		}

        private void Close()
        {
			lock (_timerLocker)
			{
				if (_cancellationTimer != null)
				{
					_cancellationTimer.Dispose();
					_cancellationTimer = null;
				}
			}

        	Glass.Remove(this);

            _currentState = OrderState.Closed;
            StateChanged.SafeInvoke(this, EventArgs.Empty);
        }

		#endregion
	}
}
